home *** CD-ROM | disk | FTP | other *** search
/ Disc to the Future 2 / Disc to the Future Part II Programmer's Reference (Wayzata Technology)(6013)(1992).bin / MAC / THINKC / TCL1 / CPOPUPME / CPOPUPME.C < prev    next >
Text File  |  1989-08-20  |  16KB  |  579 lines

  1. /*
  2.     CPopupMenu - handles a popup menu.
  3.     
  4.     by Dan Podwall (CIS: 70641,145). You may give this code away
  5.         freely, but you may not charge for it, other than normal
  6.         network download charges.
  7.     
  8.     Displays a standard popup menu title and the currently selected
  9.     item. When clicked handles menu selection.
  10.     
  11.     There are two basic modes of operation.
  12.      
  13.     1) When multiSelect is FALSE, only one item may be selected at a 
  14.         time. Selecting an item other than the currently selected one
  15.         deselects the previous one and selects the new one. Selecting 
  16.         the already selected item does nothing. This is the type of 
  17.         behaviour you might expect for a font menu, for example. 
  18.         Think of this like a radio button group. This is the default state
  19.         when the popup is created.
  20.         
  21.     2) When multiSelect is TRUE, multiple items may be checked (selected) 
  22.         simultaneously, as in a style menu. In this case, selections toggle,
  23.         i.e. selecting an already selected item deselects (unchecks) it.
  24.         This works more like a group of checkboxes.
  25.         
  26.         NOTE: The Draw() method does not currently handle multiple selections,
  27.             it only draws the first selected item. That is left as an exercise
  28.             for the reader. :)
  29.         
  30.     When useWinFont is TRUE, the title is drawn in the window's current font
  31.     rather than the system font. Note that this involves a temporary patch to
  32.     TextFont. If that makes you unhappy, then don't use it.
  33. */
  34.  
  35. #include "CPopupMenu.h"
  36. #include "CBartender.h"
  37. #include "Commands.h"
  38. #include "StringUtil.h"
  39. #include "CError.h"
  40.  
  41. extern CBartender    *gBartender;
  42. extern CError        *gError;
  43.  
  44. #define kAltCheckMark    ((unsigned char) '├')    /* ASCII 12 works for Chicago only */
  45.  
  46.  
  47. #ifndef _TextFont
  48. #define _TextFont 0xA887
  49. #endif
  50.  
  51. static Int16    menuFont = 0;
  52. static Int32    realTextFont = 0L;
  53. static void TextFontPatch( void);
  54. /*****************************************************************************/
  55. void CPopupMenu::IPopupMenu( Int16 menuID,CView *anEnclosure, CBureaucrat *aSupervisor,
  56.                     Int16 aWidth, Int16 aHeight, Int16 aHEncl, Int16 aVEncl,
  57.                     Int16 isCommandMenu)
  58. {        
  59.     CPane::IPane( anEnclosure, aSupervisor, aWidth, aHeight, aHEncl, aVEncl,
  60.                     sizFIXEDSTICKY, sizFIXEDSTICKY);
  61.     multiSelect = FALSE;
  62.     useWinFont = FALSE;
  63.     this->isCommandMenu = isCommandMenu;
  64.     IPopupMenuX( menuID);
  65.         
  66. }    /* CPopupMenu::IPopupMenu */
  67. /*****************************************************************************/
  68. void CPopupMenu::IViewTemp(CView *anEnclosure, CBureaucrat *aSupervisor,
  69.                     Ptr viewData)
  70. {
  71.     register tPopupTempPtr template = (tPopupTempPtr) viewData;
  72.     
  73.     inherited::IViewTemp( anEnclosure, aSupervisor, viewData);
  74.     
  75.     useWinFont = template->useWinFont != 0;
  76.     autoSelect = template->autoSelect != 0;
  77.     multiSelect = template->multiSelect != 0;
  78.     isCommandMenu = template->isCommandMenu != 0;
  79.     
  80.     IPopupMenuX( template->menuID);
  81.  
  82. }    /* CPopupMenu::IViewTemp */
  83. /*****************************************************************************/
  84. void CPopupMenu::IPopupMenuX( Int16 menuID)
  85. {
  86.     MenuHandle theMenu;
  87.     Int16    menuIndex, err;
  88.  
  89.     if (isCommandMenu)
  90.     {
  91.         /*
  92.             has it previously been registered by bartender?
  93.             We only want to register it with the bartender 1 time.
  94.         */
  95.         menuIndex = gBartender->FindMenuIndex( menuID);
  96.         if (menuIndex < 0)    /* no it hasn't */
  97.             gBartender->AddMenu( menuID, FALSE, -1);
  98.             gBartender->SetDimOption( menuID, dimNONE);
  99.     }
  100.     /*
  101.         copy the menu so multiple instances of the same popup menu
  102.         don't conflict with each other.
  103.     */
  104.     itsMenu = NIL;            
  105.     theMenu = GetMenu( menuID);
  106.     CheckResource( (Handle) theMenu);
  107.     HandToHand(&theMenu);
  108.     err = MemError();
  109.     if (!gError->CheckOSError( err))
  110.         gError->SevereMacError( err);
  111.  
  112.     /*
  113.         Note that we don't dispose of the original menu resource here, even
  114.         though we don't use it anymore. That's because the Bartender still
  115.         has a handle to it.
  116.     */
  117.     
  118.     HNoPurge( theMenu);
  119.     MoveHHi( theMenu);
  120.     itsMenu = theMenu;
  121.     
  122.     /* setup runtime defaults */
  123.     UncheckAllItems();
  124.     enabled = TRUE;
  125.     wantsClicks = TRUE;
  126.     autoSelect = TRUE;
  127.     firstSelection = 0;
  128.  
  129. }    /* CPopupMenu::IPopupMenuX */
  130. /*****************************************************************************/
  131. void CPopupMenu::Draw(Rect *area)
  132. {
  133.     MenuHandle    theMenu;
  134.     Int16        currItem, savedFont, savedSize, savedStyle;
  135.     Rect        r, itemRect;
  136.     FontInfo    fInfo;
  137.     Point        loc;
  138.     PenState    savedState;
  139.     
  140.     if (!itsMenu) return;
  141.     theMenu = itsMenu;
  142.     r = frame;
  143.     
  144.     if (!useWinFont)
  145.     {
  146.         savedFont = thePort->txFont;
  147.         savedSize = thePort->txSize;
  148.         savedStyle = thePort->txFace;
  149.         TextFace(0);
  150.         TextSize(12);
  151.         TextFont(0);
  152.     }
  153.     
  154.     GetPenState(&savedState);
  155.     GetFontInfo(&fInfo);
  156.         
  157.     MoveTo(1,fInfo.ascent+1);
  158.     
  159.     HLock( theMenu);
  160.     DrawString((**theMenu).menuData);
  161.     HUnlock( theMenu);    
  162.     
  163.     GetPen( &loc);
  164.     loc.h += 5;
  165.     
  166.     r.left = loc.h - 2; r.bottom--; r.right--;
  167.     EraseRect( &r);
  168.  
  169.     /* setup rect for selected items and draw them */
  170.     
  171.     itemRect.left = loc.h; itemRect.top = r.top +1;
  172.     itemRect.right = r.right -1; itemRect.bottom = r.bottom -1;
  173.     
  174.     DrawSelectedItems( &itemRect, &fInfo);
  175.  
  176.     /* draw box around current selection */
  177.     PenSize(1,1);
  178.     FrameRect(&r);
  179.     MoveTo(r.left + 1,r.bottom);
  180.     LineTo(r.right,r.bottom);
  181.     MoveTo(r.right,r.top + 1);
  182.     LineTo(r.right,r.bottom);
  183.     if (!enabled)
  184.     {    
  185.         PenPat( gray);
  186.         PenMode(patBic);
  187.         r = frame;
  188.         PaintRect(&r);
  189.     }
  190.     SetPenState(&savedState);
  191.     if (!useWinFont)
  192.     {
  193.         TextFont( savedFont);
  194.         TextSize( savedSize);
  195.         TextFace( savedStyle);
  196.     }    
  197.  
  198. }    /* CPopupMenu::Draw */
  199. /*****************************************************************************/
  200. void CPopupMenu::DrawSelectedItems( Rect *itemRect, FontInfo *fInfo)
  201. /*
  202.     draw the checked item(s). This implementation only draws the first
  203.     item, even if more than one is checked. Users most override to handle
  204.     multiple selections.
  205.     itemRect is the rect that this method must draw within. It has
  206.         already been erased.
  207.     fInfo is a FontInfo record for the font being used.
  208.     
  209.     EllipseString is is imported from StringUtil.c
  210.     
  211. */
  212. {    Str255    s;
  213.     Int16    width, checkWidth;
  214.     
  215.     if (firstSelection > 0)
  216.     {    
  217.         /* get width available to draw current selection, take into
  218.             account space for checkmark */
  219.             
  220.         if (useWinFont)
  221.             checkWidth = CharWidth( kAltCheckMark) + 4;
  222.         else checkWidth = CharWidth( checkMark);
  223.         
  224.         width = itemRect->right - itemRect->left - checkWidth;
  225.         if (width <= 0)    return;
  226.  
  227.         GetItem( itsMenu, firstSelection, s);
  228.         EllipseString( s, width);
  229.         MoveTo( itemRect->left + checkWidth, itemRect->top + fInfo->ascent);
  230.         DrawString( s);
  231.     }
  232.  
  233. }    /* DrawSelectedItems */
  234. /*****************************************************************************/
  235. void CPopupMenu::SelectItem( Int16 itemNum,tPMSelectAction actionType)
  236. {
  237.     Rect    r;
  238.     MenuHandle menu = itsMenu;
  239.     
  240.     if ((itemNum <= 0)||(itemNum > CountMItems( menu))) return;
  241.     
  242.     if (multiSelect)
  243.     {
  244.         if (actionType == pmToggle) 
  245.             CheckMenuItem( itemNum, !ItemIsChecked( itemNum));
  246.         else CheckMenuItem( itemNum, actionType == pmForceOn);
  247.         
  248.         firstSelection = GetCheckedItem(); 
  249.     }
  250.     else    
  251.     {
  252.         /*
  253.             For single selection menus, we must modify the requested action to
  254.             ensure that there is always a selected item.
  255.             Note that we must ignore pmForceOff completely for this reason, 
  256.             and pmForceOn accomplishes turning off the old selection and
  257.             turning on the new one.
  258.         */
  259.         if ((actionType == pmForceOff)||(itemNum == firstSelection)) return;
  260.         
  261.         CheckMenuItem(  firstSelection, FALSE);
  262.         CheckMenuItem( itemNum, TRUE);
  263.         firstSelection = itemNum;
  264.     }
  265.     Refresh();
  266.  
  267. }    /* CPopupMenu::SelectItem */
  268. /*****************************************************************************/
  269. void  CPopupMenu::SelectItemName( StringPtr name, tPMSelectAction actionType)
  270. /*
  271.     select item by name instead of menu item number.
  272. */
  273. {    Int16    i, itemCount = CountMItems( itsMenu);
  274.     Str255    itemString;
  275.  
  276.     for (i = 1; i <= itemCount; i++)
  277.     {
  278.         GetItem( itsMenu, i, itemString);
  279.         if (EqualString( name, itemString, FALSE, FALSE))
  280.         {
  281.             SelectItem( i, actionType);
  282.             break;
  283.         }
  284.     }
  285. }    /* CPopupMenu::SelectItemName */
  286. /*****************************************************************************/
  287. void CPopupMenu::DoClick(Point hitPt, Int16 modifierKeys, Int32 when)
  288. {
  289.     register MenuHandle    menu;
  290.     long     menuResult;
  291.     Rect    r,promptRect;
  292.     Int16    currItem,width,menuID, newItem;
  293.     Int16    savedFont, savedSize, savedStyle;
  294.     Int32     command;
  295.     char    checkChar;
  296.     
  297.     if (!itsMenu) return;
  298.     menu = itsMenu;
  299.     
  300.     if (!useWinFont)
  301.     {
  302.         savedFont = thePort->txFont;
  303.         savedSize = thePort->txSize;
  304.         savedStyle = thePort->txFace;
  305.         TextFace(0);
  306.         TextSize(12);
  307.         TextFont(0);
  308.         checkChar = checkMark;
  309.     }
  310.     else checkChar = kAltCheckMark;
  311.     
  312.     r = frame;
  313.     promptRect = r; promptRect.bottom --;
  314.     menuID = (**menu).menuID;
  315.     currItem = firstSelection; /* only 1st checked item if multiple */
  316.     
  317.     HLock( menu);
  318.     width = StringWidth((**menu).menuData) + 5;
  319.     HUnlock( menu);
  320.     r.left += width; promptRect.right = r.left - 2;
  321.     LocalToGlobal(&topLeft(r));
  322.     InsertMenu(menu,-1);
  323.     CalcMenuSize(menu);
  324.     SetHiliteMode; InvertRect( &promptRect);
  325.     
  326.     if (useWinFont)
  327.     {
  328.         menuFont = macPort->txFont;
  329.         realTextFont = NGetTrapAddress( _TextFont & 0x3FF, ToolTrap);
  330.         NSetTrapAddress( TextFontPatch, _TextFont & 0x3FF, ToolTrap);
  331.     }
  332.     menuResult = PopUpMenuSelect(menu, r.top + 1, r.left, currItem);
  333.     
  334.     if (useWinFont)
  335.     {
  336.         NSetTrapAddress( realTextFont, _TextFont & 0x3FF, ToolTrap);
  337.         realTextFont = NIL;
  338.     }
  339.     
  340.     SetHiliteMode; InvertRect( &promptRect);
  341.     DeleteMenu(menuID);
  342.     
  343.     /*
  344.         Two separate things need to be done:
  345.         1) if autoSelect is active, we handle selecting a new item. We
  346.             must check that that (hiword(menuResult) == menuID) because
  347.             a submenu item might have been selected. We don't handle
  348.             automatically selecting submenu items.
  349.             
  350.         2) If this is a command menu we see if the selected item
  351.             has a command. In this case, we do handle submenus,
  352.             i.e. submenus can have commands attached to menu items.
  353.     */
  354.     newItem = loword( menuResult);
  355.     if ((autoSelect) && (hiword(menuResult) == menuID))
  356.         SelectItem( newItem, pmToggle);
  357.  
  358.     menuID = hiword(menuResult);  /* could be submenu */
  359.     if (isCommandMenu)
  360.     {
  361.         command = gBartender->FindCmdNumber( menuID, newItem);
  362.         /*
  363.             FindCmdNumber will return a positive integer if 
  364.             there is a command for this item or a negative number
  365.             if the menu was registered but the item has no command.
  366.             If the menu was not registered it will return cmdNull;
  367.         */
  368.         if (command != cmdNull)
  369.             itsSupervisor->DoCommand( command);
  370.     }
  371.     if (!useWinFont)
  372.     {
  373.         TextFont( savedFont);
  374.         TextSize( savedSize);
  375.         TextFace( savedStyle);
  376.     }    
  377.     
  378. }    /* CPopupMenu::DoClick */
  379. /*****************************************************************************/
  380. Boolean CPopupMenu::ItemIsChecked(Int16 item)
  381. /*
  382.     return TRUE if item in menu has checkmark.
  383. */
  384. {
  385.     Int16    markChar;
  386.     MenuHandle menu;
  387.     
  388.     menu = itsMenu;
  389.     if (!menu) return FALSE;
  390.     
  391.     GetItemMark(menu, item, &markChar);
  392.     return ((markChar == checkMark)||(markChar == kAltCheckMark));
  393.  
  394. }    /* CPopupMenu::ItemIsChecked */
  395. /*****************************************************************************/
  396. Int16 CPopupMenu::GetCheckedItem()
  397. /*
  398.     return item number if any item in menu
  399.     has a checkmark, else returns 0 (FALSE).
  400. */
  401. {    Int16    i, count;
  402.     MenuHandle menu = itsMenu;
  403.  
  404.     if (!menu) return 0;
  405.     
  406.     count = CountMItems( menu);
  407.     for (i = 1; i <= count; i++)
  408.     {
  409.         if (ItemIsChecked( i)) return i;
  410.     }
  411.     return 0;
  412.  
  413. }    /* CPopupMenu::GetCheckedItem */
  414. /*****************************************************************************/
  415. void CPopupMenu::UncheckAllItems( void)
  416. {
  417.     Int16    i, count;
  418.     MenuHandle menu = itsMenu;
  419.  
  420.     if (!itsMenu) return;
  421.     
  422.     count = CountMItems( menu);
  423.     for (i = 1; i <= count; i++)
  424.         CheckMenuItem( i, FALSE);
  425.         
  426.     firstSelection = 0;
  427.     
  428. }    /* CPopupMenu::UncheckAllItems */
  429. /*****************************************************************************/
  430. void CPopupMenu::AppendItem( StringPtr newItem)
  431. {
  432.     MenuHandle menu = itsMenu;
  433.     
  434.     if (!itsMenu) return;
  435.     
  436.     AppendMenu( menu, newItem);
  437.  
  438. }    /* CPopupMenu::AppendItem */
  439. /*****************************************************************************/
  440. void CPopupMenu::InsertItem(StringPtr string, Int16 afterItem)
  441. {
  442.     MenuHandle menu = itsMenu;
  443.     
  444.     if (!itsMenu) return;
  445.     
  446.     InsMenuItem( menu, string, afterItem);
  447.  
  448. }    /* CPopupMenu::InsertItem */
  449. /*****************************************************************************/
  450. void CPopupMenu::DeleteItem( Int16 itemNum)
  451. {
  452.     MenuHandle menu = itsMenu;
  453.     Rect    r;
  454.     
  455.     if (!itsMenu) return;
  456.     
  457.     if ((itemNum > 0) && (itemNum <= CountMItems( menu)))
  458.     {
  459.         DelMenuItem( menu, itemNum);
  460.         if (itemNum == firstSelection)
  461.         {
  462.             firstSelection = GetCheckedItem();
  463.             if (macPort == thePort)
  464.             {
  465.                 r = frame;
  466.                 InvalRect( &r);
  467.             }
  468.         }
  469.     }
  470.  
  471. }    /* CPopupMenu::DeleteItem */
  472. /*****************************************************************************/
  473. void CPopupMenu::DeleteAllItems()
  474. {
  475.     MenuHandle menu = itsMenu;
  476.     Int16    i, count;
  477.     
  478.     if (!itsMenu) return;
  479.     
  480.     count = CountMItems( menu);
  481.     for ( i = count; i > 0; i--)
  482.     {
  483.         DelMenuItem( menu, i);
  484.     }
  485.     firstSelection = 0;
  486.  
  487. }    /* CPopupMenu::DeleteAllItems */
  488. /*****************************************************************************/
  489. void CPopupMenu::SetItemString( Int16 item, StringPtr string)
  490. {
  491.     if (!itsMenu) return;
  492.     
  493.     SetItem( itsMenu, item, string);
  494.     
  495.     if (ItemIsChecked( item)) Refresh();
  496.     
  497. }    /* CPopupMenu::SetItemString */
  498. /*****************************************************************************/
  499. void CPopupMenu::SetCurrItemString( StringPtr string)
  500. {
  501.     SetItemString( GetCheckedItem(), string);
  502.  
  503. }    /* CPopupMenu::SetCurrItemString */
  504. /*****************************************************************************/
  505. void CPopupMenu::GetItemString( Int16 item, StringPtr string)
  506. {
  507.     GetItem( itsMenu, item, string);
  508.  
  509. }    /* CPopupMenu::GetItemString */
  510. /*****************************************************************************/
  511. void CPopupMenu::GetCurrItemString( StringPtr string)
  512. {
  513.     GetItemString( GetCheckedItem(), string);
  514.  
  515. }    /* CPopupMenu::GetCurrItemString */
  516. /*****************************************************************************/
  517. void CPopupMenu::SetAutoSelect( Int16 selectFlag)
  518. {
  519.     autoSelect = selectFlag;
  520.  
  521. }    /*  CPopupMenu::SetAutoSelect */
  522. /*****************************************************************************/
  523. void CPopupMenu::CheckMenuItem( Int16 item, Int16 checkFlag)
  524. {
  525.     Int16 cmdChar;
  526.     
  527.     if (!itsMenu) return;
  528.     
  529.     /*    make sure that item does not have hierarchical menu,
  530.         otherwise clearing item mark would lose the submenu */
  531.     
  532.     GetItemCmd( itsMenu, item, &cmdChar);
  533.     if (cmdChar == hMenuCmd) return;
  534.     
  535.     if (checkFlag)
  536.     {
  537.         char    checkChar = useWinFont? kAltCheckMark : checkMark;
  538.         SetItemMark( itsMenu, item, checkChar);
  539.     }
  540.     else SetItemMark( itsMenu, item, 0);
  541.  
  542. }    /* CPopupMenu::CheckMenuItem */
  543. /*****************************************************************************/
  544. void CPopupMenu::SetEnable( Int16 enableFlag)
  545. {
  546.     if (enableFlag != enabled)
  547.     {
  548.         enabled = enableFlag;
  549.         SetWantsClicks( enabled);
  550.         Refresh();
  551.     }
  552.  
  553. }    /* CPopupMenu::SetEnable */
  554. /*****************************************************************************/
  555. Boolean CPopupMenu::GetEnable( void)
  556. {
  557.     return enabled;
  558.  
  559. }    /* CPopupMenu::GetEnable */
  560. /*****************************************************************************/
  561. void CPopupMenu::Dispose( void)
  562. {
  563.     if (itsMenu) DisposeMenu( itsMenu);
  564.  
  565. }    /* CPopupMenu::Dispose */
  566. /*****************************************************************************/
  567. void TextFontPatch( void)
  568. {
  569.     asm    {
  570.         move.l        a5, a1                ; save current A5
  571.         move.l        CurrentA5, a5        ; use app's globals
  572.         move.w        menuFont, 4(sp)        ; the desired font
  573.         move.l        realTextFont, a0    ; address of _TextFont
  574.         move.l        a5, a1                ; restore previous A5
  575.         jmp            (a0)                ; go to trap
  576.     }
  577. }
  578. /*****************************************************************************/
  579.